home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / nnrpd / group.c < prev    next >
C/C++ Source or Header  |  1993-01-29  |  11KB  |  498 lines

  1. /*  $Revision: 1.13 $
  2. **
  3. **  Newsgroups and the active file.
  4. */
  5. #include "nnrpd.h"
  6. #include "mydir.h"
  7.  
  8.  
  9. /*
  10. **  Newsgroup hashing stuff.  See comments in innd/ng.c.
  11. */
  12.  
  13. #define GRP_HASH(Name, p, j)    \
  14.     for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++
  15. #define GRP_SIZE    512
  16. #define GRP_BUCKET(j)    &GRPtable[j & (GRP_SIZE - 1)]
  17.  
  18. typedef struct _GRPHASH {
  19.     int        Size;
  20.     int        Used;
  21.     GROUPENTRY    **Groups;
  22. } GRPHASH;
  23.  
  24.  
  25. STATIC GRPHASH        GRPtable[GRP_SIZE];
  26. STATIC GROUPENTRY    *GRPentries;
  27. STATIC int        GRPbuckets;
  28. STATIC int        GRPsize;
  29.  
  30.  
  31. /*
  32. **  See if a given newsgroup exists.
  33. */
  34. GROUPENTRY *
  35. GRPfind(group)
  36.     register char        *group;
  37. {
  38.     register char        *p;
  39.     register unsigned int    j;
  40.     register int        i;
  41.     register GROUPENTRY        **gpp;
  42.     GRPHASH            *htp;
  43.     char            c;
  44.  
  45.     /* SUPPRESS 6 *//* Over/underflow from plus expression */
  46.     GRP_HASH(group, p, j);
  47.     htp = GRP_BUCKET(j);
  48.     for (c = *group, gpp = htp->Groups, i = htp->Used; --i >= 0; gpp++)
  49.     if (c == gpp[0]->Name[0] && EQ(group, gpp[0]->Name))
  50.         return gpp[0];
  51.     return NULL;
  52. }
  53.  
  54.  
  55. STATIC void
  56. GRPhash()
  57. {
  58.     register char        *p;
  59.     register int        i;
  60.     register GROUPENTRY        *gp;
  61.     register unsigned int    j;
  62.     register GRPHASH        *htp;
  63.  
  64.     /* Set up the default hash buckets. */
  65.     GRPbuckets = GRPsize / GRP_SIZE;
  66.     if (GRPbuckets == 0)
  67.     GRPbuckets = 1;
  68.     if (GRPtable[0].Groups)
  69.     for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++)
  70.         htp->Used = 0;
  71.     else
  72.     for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++) {
  73.         htp->Size = GRPbuckets;
  74.         htp->Groups = NEW(GROUPENTRY*, htp->Size);
  75.         htp->Used = 0;
  76.     }
  77.  
  78.     /* Now put all groups into the hash table. */
  79.     for (i = GRPsize, gp = GRPentries; --i >= 0; gp++) {
  80.     /* SUPPRESS 6 *//* Over/underflow from plus expression */
  81.     GRP_HASH(gp->Name, p, j);
  82.     htp = GRP_BUCKET(j);
  83.     if (htp->Used >= htp->Size) {
  84.         htp->Size += GRPbuckets;
  85.         RENEW(htp->Groups, GROUPENTRY*, htp->Size);
  86.     }
  87.     htp->Groups[htp->Used++] = gp;
  88.     }
  89.  
  90.     /* Note that we don't sort the buckets. */
  91. }
  92.  
  93.  
  94. /*
  95. **  Read the active file into memory, sort it, and set the number of
  96. **  newsgroups read in.  Return TRUE if okay, FALSE on error.
  97. */
  98. BOOL
  99. GetGroupList()
  100. {
  101.     static char            *active;
  102.     register char        *p;
  103.     register char        *q;
  104.     register GROUPENTRY        *gp;
  105.     register int        i;
  106.  
  107.     /* If re-scanning, free previous groups. */
  108.     if (active != NULL) {
  109.     DISPOSE(active);
  110.     DISPOSE(GRPentries);
  111.     }
  112.  
  113.     /* Get the new file. */
  114.     active = ReadInFile(ACTIVE, (struct stat *)NULL);
  115.     if (active == NULL) {
  116.     syslog(L_ERROR, "%s cant read %s %m", ClientHost, ACTIVE);
  117.     return FALSE;
  118.     }
  119.  
  120.     /* Count lines. */
  121.     for (p = active, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++)
  122.     continue;
  123.  
  124.     /* Fill in the group array. */
  125.     GRPentries = NEW(GROUPENTRY, i);
  126.     for (i = 0, gp = GRPentries, p = active; *p; i++, gp++, p = q + 1) {
  127.     gp->Name = p;
  128.     if ((p = strchr(p, ' ')) == NULL) {
  129.         syslog(L_ERROR, "%s internal no_space1 \"%.20s...\"",
  130.         ClientHost, gp->Name);
  131.         return FALSE;
  132.     }
  133.     *p++ = '\0';
  134.  
  135.     /* Get the high mark. */
  136.     if ((q = strchr(p, ' ')) == NULL) {
  137.         syslog(L_ERROR, "%s internal no_space2 \"%.20s...\"",
  138.         ClientHost, gp->Name);
  139.         return FALSE;
  140.     }
  141.     *q++ = '\0';
  142.     gp->High = atol(p);
  143.  
  144.     /* Get the low mark. */
  145.     if ((p = strchr(q, ' ')) == NULL) {
  146.         syslog(L_ERROR, "%s internal no_space3 \"%.20s...\"",
  147.         ClientHost, gp->Name);
  148.         return FALSE;
  149.     }
  150.     *p++ = '\0';
  151.     gp->Low = atol(q);
  152.  
  153.     /* Kill the newline. */
  154.     if ((q = strchr(p, '\n')) == NULL) {
  155.         syslog(L_ERROR, "%s internal newline \"%.20s...\"",
  156.         ClientHost, gp->Name);
  157.         return FALSE;
  158.     }
  159.     *q = '\0';
  160.     gp->Flag = *p;
  161.     gp->Alias = gp->Flag == NF_FLAG_ALIAS ? p + 1 : NULL;
  162.     }
  163.  
  164.     GRPsize = i;
  165.     GRPhash();
  166.     return TRUE;
  167. }
  168.  
  169.  
  170. /*
  171. **  Sorting predicate to put newsgroup names into numeric order.
  172. */
  173. STATIC int
  174. ARTcompare(p1, p2)
  175.     POINTER    p1;
  176.     POINTER    p2;
  177. {
  178.     ARTNUM    *i1;
  179.     ARTNUM    *i2;
  180.  
  181.     i1 = CAST(ARTNUM*, p1);
  182.     i2 = CAST(ARTNUM*, p2);
  183.     return *i1 - *i2;
  184. }
  185.  
  186.  
  187. /*
  188. **  Fill in ARTnumbers with the numbers of the articles in the current
  189. **  group.
  190. */
  191. STATIC void
  192. GRPscandir(dir)
  193.     char        *dir;
  194. {
  195.     static char        SPOOL[] = _PATH_SPOOL;
  196.     static int        ARTarraysize;
  197.     register DIRENTRY    *ep;
  198.     register DIR    *dp;
  199.     register char    *p;
  200.     register ARTNUM    i;
  201.  
  202.     /* Go to the directory. */
  203.     if (chdir(SPOOL) < 0) {
  204.     syslog(L_FATAL, "%s cant cd %s %m", ClientHost, SPOOL);
  205.     ExitWithStats(1);
  206.     }
  207.  
  208.     if (ARTarraysize == 0) {
  209.     ARTarraysize = 1024;
  210.     ARTnumbers = NEW(ARTNUM, ARTarraysize);
  211.     }
  212.  
  213.     /* The newsgroup directory might not exist; treat it as empty. */
  214.     ARTsize = 0;
  215.     GRPcount++;
  216.     if (chdir(dir) < 0)
  217.     return;
  218.     dp = opendir(".");
  219.     if (dp == NULL) {
  220.     syslog(L_ERROR, "%s cant opendir %s %m", ClientHost, dir);
  221.     return;
  222.     }
  223.  
  224.     while ((ep = readdir(dp)) != NULL) {
  225.     /* Get the numeric value of the filename, if it's all digits. */
  226.     for (p = ep->d_name, i = 0; *p; p++) {
  227.         if (!CTYPE(isdigit, *p))
  228.         break;
  229.         i = i * 10 + *p - '0';
  230.     }
  231.     if (*p || i == 0)
  232.         continue;
  233.  
  234.     if (ARTsize + 1 >= ARTarraysize) {
  235.         ARTarraysize += 1024;
  236.         RENEW(ARTnumbers, ARTNUM, ARTarraysize);
  237.     }
  238.  
  239.     ARTnumbers[ARTsize++] = i;
  240.     }
  241.     (void)closedir(dp);
  242.  
  243.     ARTcache = NULL;
  244.     qsort((POINTER)ARTnumbers, (SIZE_T)ARTsize, sizeof ARTnumbers[0],
  245.     ARTcompare);
  246. }
  247.  
  248.  
  249. /*
  250. **  Change to or list the specified newsgroup.  If invalid, stay in the old
  251. **  group.
  252. */
  253. FUNCTYPE
  254. CMDgroup(ac, av)
  255.     int            ac;
  256.     char        *av[];
  257. {
  258.     static time_t    last_time;
  259.     static char        NOSUCHGROUP[] = NNTP_NOSUCHGROUP;
  260.     register char    *p;
  261.     register int    i;
  262.     time_t        now;
  263.     char        *grplist[2];
  264.     char        *group;
  265.     char        buff[SPOOLNAMEBUFF];
  266.  
  267.     if (!PERMcanread) {
  268.     Reply("%s\r\n", NOACCESS);
  269.     return;
  270.     }
  271.  
  272.     /* Parse arguments. */
  273.     if (ac == 1) {
  274.     if (GRPcount == 0) {
  275.         Printf("%d No group specified\r\n", NNTP_XGTITLE_BAD);
  276.         return;
  277.     }
  278.     (void)strcpy(buff, GRPlast);
  279.     for (p = buff; *p; p++)
  280.         if (*p == '/')
  281.         *p = '.';
  282.     group = buff;
  283.     }
  284.     else
  285.     group = av[1];
  286.     if (GRPfind(group) == NULL) {
  287.     Reply("%s\r\n", NOSUCHGROUP);
  288.     return;
  289.     }
  290.  
  291.     /* If permission is denied, pretend group doesn't exist. */
  292.     if (PERMspecified) {
  293.     grplist[0] = group;
  294.     grplist[1] = NULL;
  295.     if (!PERMmatch(PERMdefault, PERMlist, grplist)) {
  296.         Reply("%s\r\n", NOSUCHGROUP);
  297.         return;
  298.     }
  299.     }
  300.     else if (!PERMdefault) {
  301.     Reply("%s\r\n", NOSUCHGROUP);
  302.     return;
  303.     }
  304.  
  305.     /* Close out any existing article, report group stats. */
  306.     ARTclose();
  307.     ARTindex = 0;
  308.     GRPreport();
  309.  
  310.     /* Make the group name a directory name. */
  311.     (void)strcpy(buff, group);
  312.     for (p = buff; *p; p++)
  313.     if (*p == '.')
  314.         *p = '/';
  315.  
  316.     /* If we haven't been in the group recently, rescan. */
  317.     (void)time(&now);
  318.     if (!EQ(buff, GRPlast) || now > last_time + NNRP_RESCAN_DELAY) {
  319.     GRPscandir(buff);
  320.     (void)strcpy(GRPlast, buff);
  321.     last_time = now;
  322.     }
  323.  
  324.     /* Close down any overview file. */
  325.     OVERclose();
  326.  
  327.     /* Doing a "group" command? */
  328.     if (caseEQ(av[0], "group")) {
  329.     if (ARTsize == 0)
  330.         Reply("%d 0 0 0 %s\r\n", NNTP_GROUPOK_VAL, group);
  331.     else
  332.         Reply("%d %d %ld %ld %s\r\n",
  333.         NNTP_GROUPOK_VAL,
  334.         ARTsize, ARTnumbers[0], ARTnumbers[ARTsize - 1], group);
  335.     }
  336.     else {
  337.     /* Must be doing a "listgroup" command. */
  338.     Reply("%d Article list follows\r\n", NNTP_GROUPOK_VAL);
  339.     for (i = 0; i < ARTsize; i++)
  340.         Printf("%ld\r\n", ARTnumbers[i]);
  341.     Printf(".\r\n");
  342.     }
  343. }
  344.  
  345.  
  346. /*
  347. **  Report on the number of articles read in the group, and clear the count.
  348. */
  349. void
  350. GRPreport()
  351. {
  352.     register char    *p;
  353.     char        buff[SPOOLNAMEBUFF];
  354.  
  355.     if (GRPlast[0] && GRParticles != 0) {
  356.     (void)strcpy(buff, GRPlast);
  357.     for (p = buff; *p; p++)
  358.         if (*p == '/')
  359.         *p = '.';
  360.     syslog(L_NOTICE, "%s group %s %ld", ClientHost, buff, GRParticles);
  361.     GRParticles = 0;
  362.     }
  363. }
  364.  
  365.  
  366. /*
  367. **  Used by ANU-News clients.
  368. */
  369. FUNCTYPE
  370. CMDxgtitle(ac, av)
  371.     int            ac;
  372.     char        *av[];
  373. {
  374.     register QIOSTATE    *qp;
  375.     register char    *line;
  376.     register char    *p;
  377.     register char    *q;
  378.     char        save;
  379.  
  380.     /* Parse the arguments. */
  381.     if (ac == 1) {
  382.     if (GRPcount == 0) {
  383.         Printf("%d No group specified\r\n", NNTP_XGTITLE_BAD);
  384.         return;
  385.     }
  386.     p = GRPlast;
  387.     }
  388.     else
  389.     p = av[1];
  390.  
  391.     /* Open the file, get ready to scan. */
  392.     if ((qp = QIOopen(NEWSGROUPS, QIO_BUFFER)) == NULL) {
  393.     syslog(L_ERROR, "%s cant open %s %m", ClientHost, NEWSGROUPS);
  394.     Printf("%d Can't open %s\r\n", NNTP_XGTITLE_BAD, NEWSGROUPS);
  395.     return;
  396.     }
  397.     Printf("%d list follows\r\n", NNTP_XGTITLE_OK);
  398.  
  399.     /* Print all lines with matching newsgroup name. */
  400.     while ((line = QIOread(qp)) != NULL) {
  401.     for (q = line; *q && !ISWHITE(*q); q++)
  402.         continue;
  403.     save = *q;
  404.     *q = '\0';
  405.     if (wildmat(line, p)) {
  406.         *q = save;
  407.         Printf("%s\r\n", line);
  408.     }
  409.     }
  410.  
  411.     /* Done. */
  412.     QIOclose(qp);
  413.     Printf(".\r\n");
  414. }
  415.  
  416.  
  417. #if    defined(DO_DO_XTHREAD)
  418. /*
  419. **  XTHREAD command.  Based on code by Tim Iverson <iverson@xstor.com>,
  420. **  Wayne Davison <davison@borland.com>, and Rob Robertson
  421. **  <rob@violet.berkeley.edu>.  Usage:
  422. **    xthread [thread]    Dump thread file for current group.
  423. **    xthread dbinit        Dump db.init file.
  424. **  This is a very ugly command -- data is raw binary.
  425. */
  426. FUNCTYPE
  427. CMDxthread(ac, av)
  428.     int        ac;
  429.     char    *av[];
  430. {
  431.     static char        NOTAVAIL[] = "%d %s not available.\r\n";
  432.     static char        USAGE[] = "[dbinit|thread]";
  433.     struct stat        Sb;
  434.     register FILE    *F;
  435.     register int    i;
  436.     char        buff[BUFSIZ];
  437.     char        *file;
  438. #if    defined(THREAD_NAMES_FLAT)
  439.     register char    *p;
  440.     char        temp[SPOOLNAMEBUFF];
  441. #endif    /* defined(THREAD_NAMES_FLAT) */
  442.  
  443.     if (!PERMcanread) {
  444.     Reply("%s\r\n", NOACCESS);
  445.     return;
  446.     }
  447.  
  448.     /* Parse the arguments. */
  449.     if (ac == 1 || (ac == 2 && caseEQ(av[1], "thread"))) {
  450.     if (GRPcount == 0) {
  451.         Reply("%s\r\n", NNTP_NOTINGROUP);
  452.         return;
  453.     }
  454. #if    defined(THREAD_NAMES_FLAT)
  455.     (void)strcpy(temp, GRPlast);
  456.     for (p = temp; *p; p++)
  457.         if (*p == '/')
  458.         *p = '.';
  459.     (void)sprintf(buff, "%s/%s%s", THREAD_DIR, temp, THREAD_SUFFIX);
  460. #else
  461.     (void)sprintf(buff, "%s/%s%s", THREAD_DIR, GRPlast, THREAD_SUFFIX);
  462. #endif    /* defined(THREAD_NAMES_FLAT) */
  463.     file = buff;
  464.     }
  465.     else if (ac == 2 && caseEQ(av[1], "dbinit"))
  466.     file = THREAD_DB;
  467.     else {
  468.     Reply("%d Usage: %s\r\n", NNTP_SYNTAX_VAL, USAGE);
  469.     return;
  470.     }
  471.  
  472.     /* Open the thread file, say what's coming. */
  473.     if ((F = fopen(file, "r")) == NULL)  {
  474.     syslog(L_ERROR, "%s cant fopen %s %m", ClientHost, file);
  475.     Reply(NOTAVAIL, NNTP_TEMPERR_VAL, file);
  476.     return;
  477.     }
  478.  
  479.     /* Get file size. */
  480.     if (fstat(fileno(F), &Sb) < 0) {
  481.     syslog(L_ERROR, "%s cant fstat %s %m", ClientHost, file);
  482.     Reply(NOTAVAIL, NNTP_TEMPERR_VAL, file);
  483.     (void)fclose(F);
  484.     return;
  485.     }
  486.     Reply("%d %ul binary bytes follow\r\n",
  487.     THREAD_NNTP_CODE, (unsigned long)Sb.st_size);
  488.  
  489.     /* Send the data.  Ignore errors since there is no way to put
  490.      * that info in the output stream -- symptomatic of binary
  491.      * data formats. */
  492.     while ((i = fread(buff, (SIZE_T)1, (SIZE_T)sizeof buff, F)) > 0)
  493.     (void)fwrite(buff, (SIZE_T)i, (SIZE_T)1, stdout);
  494.     (void)fclose(F);
  495.     Printf("\r\n.\r\n");
  496. }
  497. #endif    /* defined(DO_DO_XTHREAD) */
  498.